package m184tom185

import (
	"context"

	"github.com/pkg/errors"
	"github.com/stackrox/rox/generated/storage"
	permissionSetStore "github.com/stackrox/rox/migrator/migrations/m_184_to_m_185_remove_policy_vulnerability_report_resources/permissionsetstore"
	"github.com/stackrox/rox/migrator/types"
	"github.com/stackrox/rox/pkg/sac"
)

const (
	batchSize = 500

	// policy resource to replace.
	policy = "Policy"

	// vulnerabilityReport resource to replace.
	vulnerabilityReport = "VulnerabilityReports"

	// workflowAdministration resource is the replacement resource.
	workflowAdministration = "WorkflowAdministration"
)

func migrate(database *types.Databases) error {
	ctx := sac.WithAllAccess(context.Background())

	permissionSetStorage := permissionSetStore.New(database.PostgresDB)
	permissionSetsToMigrate := make([]*storage.PermissionSet, 0, batchSize)
	err := permissionSetStorage.Walk(ctx, func(obj *storage.PermissionSet) error {
		var hasReplacedResource bool
		for resource, access := range obj.GetResourceToAccess() {
			switch resource {
			case policy, vulnerabilityReport:
				// In case we have found a permission set with either the policy resource or the vulnerability report
				// resource, we have to replace both with the WorkflowAdministration resource.
				hasReplacedResource = true

				accessForWorkflowAdministration, found := obj.GetResourceToAccess()[workflowAdministration]
				// In case the permission set currently does not have permissions to the WorkflowAdministration
				// resource, we will add the current level of the resource to replace to the permission set.
				if !found {
					accessForWorkflowAdministration = access
				} else if access < accessForWorkflowAdministration {
					// In case the permission set does have permission to the WorkflowAdministration resource _and_
					// the current access for the resource to replace is lower than the one of the WorkflowAdministration,
					// we will assign it the lower access.
					// The reasoning behind is that we do not want to grant permission sets more permissions that they
					// ideally require. The disadvantage is that this might impact customers that did not change their
					// permission sets during the deprecation phase but want more permissions on the resource.
					accessForWorkflowAdministration = access
				}
				obj.ResourceToAccess[workflowAdministration] = accessForWorkflowAdministration
				delete(obj.GetResourceToAccess(), resource)
			default:
				continue
			}
		}

		if !hasReplacedResource {
			return nil
		}

		permissionSetsToMigrate = append(permissionSetsToMigrate, obj)
		if len(permissionSetsToMigrate) >= batchSize {
			if err := permissionSetStorage.UpsertMany(ctx, permissionSetsToMigrate); err != nil {
				return errors.Wrap(err, "upserting migrated permission sets")
			}
			permissionSetsToMigrate = permissionSetsToMigrate[:0]
		}
		return nil
	})
	if err != nil {
		return err
	}

	if len(permissionSetsToMigrate) > 0 {
		return permissionSetStorage.UpsertMany(ctx, permissionSetsToMigrate)
	}
	return nil
}
